1.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I9d935cf5e0c231e8cb7af5f61b9a9fc552c3521e
Code Smell Term: code smell
Code Smell Discussions: 
1)	Reviewer: please refrain from providing methods like these: get_or_create is a code smell; do either get or create, not both.
2)	Developer: I'll do
Source Code URL: https://review.opendev.org/#/c/64553/36..37/neutron/db/l3_hamode_db.py@135
2.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I9d935cf5e0c231e8cb7af5f61b9a9fc552c3521e
Code Smell Term: code smell
Code Smell Discussions: 
1)	Reviewer: An easier way to trap the exception would be:
with self.assertRaises(ValueError):
 ...
2)	Developer: The assertRaises context manager was added in 2.7, so I'll use the poor's man version for now. I knew this code smelled, but it was late and my nose wasn't as perceptive.
Source Code URL: https://review.opendev.org/#/c/64553/72..73/neutron/tests/unit/db/test_l3_ha_db.py@274
3.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~Iab6505f239d2c4c9bcbf4e32a292d7b4b5320c8e
Code Smell Term: anti-pattern
Code Smell Discussions: 
1)	Reviewer: a get that creates something is always a bit weird. anyway please just add a comment to explain the logic.
2)	Developer: agreed, this is the usual neutron anti-pattern, I'd love to get rid of it, but it's gonna take time
Source Code URL: https://review.opendev.org/#/c/102101/32..33/neutron/db/dvr_mac_db.py@112
4.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I219c6bdf63b0b5e945b655677f9e28fa591f03cd
Code Smell Term: anti-pattern
Code Smell Discussions: 
1)	Reviewer: (Suggestion only) This many mocks has to be some kind of an anti-pattern.  Consider extracting the plugin update if/else clause out of treat_devices_added_or_updated() so that it can be tested in isolation.
2)	Developer: This is a valuable suggestion that I would like to defer to a follow up patch, if you don't mind.
Source Code URL: https://review.opendev.org/#/c/61964/18..19/neutron/tests/unit/openvswitch/test_ovs_neutron_agent.py@263
5.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~Ie55fab3c53276ed48d5e9d603e8c1dc59fa9cc32
Code Smell Term: bad pattern
Code Smell Discussions: 
1)	Reviewer: I don't want to sound too pedantic but this way you're just moving the nonsense here rather than in type_vlan.py
Frankly now that I see this snippet of code, I wonder why we even bother catching an exception here to raise another one, to then catch it again and then to finally die! This is a bit convoluted to say the least!
Ultimately this is about validating configuration inputs; now...if there is no recovery action that can be implemented (i.e. fall back on a sane sets of defaults), what's the point of doing all of this? We might as well just die abruptly and let the admin see what went wrong!
I appreciate you only wanted to fix the TODO, so it's not your wrongdoing here, but adding insult to injury does not sound worth it in my opinion.
I'd suggest you to revise the entire structure of this snippet also because of the comment below:
2)	Developer: I think moving it here makes more sense than in type_vlan.py because this is where the rest of it is getting parsed.  Secondly, if we did it in type_vlan.py - we'll have to duplicate the code in other plugins.
I don't particularly fancy the idea of not doing anything because that leads to wasted time due to a misconfiguration or a typo perhaps.  If I was using this code I'd rather see something printed right away than die unceremoniously.  The point of doing this is to clearly point out there was something wrong and fail.
About throwing, catching and dying.  I think the point of that is to let the caller decide what it wants to do.  In this context perhaps it's fine to just die here because that's the only context but some one is going to point out later that this is too low level to decide the final action of parse failure. I'm fine either way on this particular one.
Let me know what the real issue is here because I think am not clear about it.  Are you suggesting that this method shouldn't be throwing exception at all?  Just die if error with proper message?  and clean up exception handling from around its call?  In this particular case that's fine too I'd say.
3)	Reviewer: All I am saying is that all this error handling is way too convoluted for the purpose that it's serving, that is to eventually die! I appreciate that this is utility code whose objective may be unknown at the time of writing, but the matter of fact is that we defined a number of exceptions, discerned so many cases to then end up doing except Exception: sys.exit(1).
There is no actual use in the entire Neutron code base where this complexity is justified. The scope of this patch  should be limited to just getting rid of the TODO, however if in the process we can make a code cleaner and more linear I'd promote this type of behavior. Now you can obviously be free to ignore my comments; my only attempt is to give the contributor (any contributor) an opportunity to reflect on the larger picture; fixing teeny bits without looking with a wider angle is one the reasons I think the neutron code base is in the state it is.
In this particular example I would do it like below. I'd rather prefer one exit point for the happy path and one exit point for the failure path. It makes the code a lot easier to read and easier to test IMO. Obviously this may become subjective so feel free to ignore whatever I said.
if network_vlan_range:
    # we won't fail here
    tokens = network_vlan_range.strip().split(':')
    if len(tokens) and tokens[0]:
        network = tokens[0]
        vlan_range = None
        if len(tokens) == 3:
            # If I fail here we don't need to care
            vlan_range = (int(tokens[1], tokens[2]))
            # we can fail here too, but who cares
            verify_vlan_range(vlan_range)
        return network, vlan_range
# Or raise whatever
raise Exception('Incomplete string')
4)	Reviewer: Armando, what is 'nonsense' here? I'm not sure I understand why you are advocated turning the method into one that raises several possible exception types instead of just one that is descriptive of the problem. This exception is logged before the sys.exit you mentioned and the approach you suggest will leak ValueErrors, etc directly to users. The error below is an awful user experience.
ValueError: need more than 2 values to unpack
Even if you don't care about user errors, limiting the possible exception types from a utility function is much easier on callers - even if there is only one now. For example, what if we do decide to use a default name instead of exiting in the future? All we would have to do is catch the name exception, which is intuitive for the programmer. With the uncaught approach you proposed, we have to catch a ValueError and discern if it's due to an unpacking failure or an int() call on a string. yuck
5)	Reviewer: Have you followed the discussion on the previous patch sets? If you did you'd know what I am referring to here :)
I might as well be mistaken, but where would my example lead to the ValueError: need more than 2 values to unpack?
There's a balance to be struck between coding for future change and simplicity. In this module I just don't see a good balance, sorry. I see  a method made of a few lines of code having 5 exit points, that they are all about broken inputs. The amount of information carried out by these Exceptions is very minimal IMO and I believe that if in the end we end up catching all errors blindly there is some bad pattern going on.
Source Code URL: https://review.opendev.org/#/c/126360/5..6/neutron/plugins/common/utils.py@64
6.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I86bdef7c9d988cb9d87c88adde55548d459f29a5
Code Smell Term: technical debt
Code Smell Discussions: 
1)	Reviewer: I am not convinced that the change in l3_agent is the right one, and consequently I am unsure that this is the right UT tweak.
Having said that, Unit Coverage has proven itself unable to catch these types of issues, we need to figure out a way of testing this at a functional level: we need to pay technical debt now, rather than later.
At least we need a bug report to track this effort and a volunteer. Would you be interested in spearheading this effort?
2)	Developer: By this you mean that we're not testing process_router in handling the various ways the router properties can change?
I can file a bug and attempt to fix the problem.  I don't have a good feel about the size of the effort yet.
3)	Reviewer: My point here is twofold: a) depending on the actual code change in l3_agent you'd need to revise the UT accordingly; b) these types of issues are not effectively tested at a unit level as they are the result of the actions of multiple components at play. Without functional coverage these issues are bound to happen again by mean of regression.
Source Code URL: https://review.opendev.org/#/c/126176/2..3/neutron/tests/unit/test_l3_agent.py@1039
7.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I4bd562e5138c2d2850072440aa121f27e902463a
Code Smell Term: technical debt
Code Smell Discussions: 
1)	Reviewer: ddmeh - a unit test that has to verify a log function has been called means that there is a function that can't be tested properly. this is another example of fine-grained technical debt we need to address. As this has nothing to do with your patch you might be annoyed by this comment. However, meh.
2)	Developer: i wanted to cry when i saw this. no idea why this was added and what it actually achieves. i guess we can clean this up at a later stage.
Source Code URL: https://review.opendev.org/#/c/94018/18/neutron/tests/unit/test_l3_agent.py@a1975
8.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I2ab37847074fa6bbdd2b13fd03b8742996dcfc78
Code Smell Term: technical debt
Code Smell Discussions: 
1)	Reviewer: Pretty weird that looping call dies when an iteration throws an exception. Ideally we'd push a new looping call (LenientFixedIntervalLoopingCall) and sync the patch to Neutron. However since we'd much prefer to swim in technical debt...
2)	Reviewer: That's the right answer, but I don't know how to contribute to that code so I did it here.
3)	Reviewer: The home of the code is oslo-incubator, you could contribute there.
https://review.openstack.org/#/q/status:open+project:openstack/oslo-incubator,n,z
4)	Developer: Now that it won't be catching all exceptions, this is no longer easily generalizable to oslo.
Source Code URL: https://review.opendev.org/#/c/124961/1..2//COMMIT_MSG@8

1.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~Idd7209299c5290424dfe7c2595334d3b2bb58a20
Code Smell Term: code smell
Code Smell Discussions: 
1)	Reviewer: why is the import located here and not at the top of the file?
2)	Developer: As far as I could ascertain while writing and testing this, the mock of the neutron.common.rpc wouldn't occur correctly if the l3_router_plugin was imported before the patch was set up.  But since this is working in my dev environment and not at all in the jenkins runs, I will have to re-visit this approach.  I am starting to wonder about the usefulness at all of these particular unit tests since, as you allude to below, what is being tested is internal details of implementation.
3)	Reviewer: Having to import here is definitely a code smell. The mock docs [1] point to why this appeared to be necessary.  The solution is to mock out n_rpc instead, e.g.
 self.patcher = mock.patch('neutron.services.l3_router.l3_router_plugin.n_rpc')
1: http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch
Source Code URL: https://review.opendev.org/#/c/154497/7..8/neutron/tests/unit/services/l3_router/test_l3_router_plugin.py@32
2.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~Ie8240884655eb002359389bdb195796970622fdc
Code Smell Term: code smell
Code Smell Discussions: 
1)	Reviewer: true. seems to me, the flag wants to be a multi-valued parameter :
Value 0: auto detect
Value 1: IPv6 enabled without detection
Value 2: IPv6 disabled without detection
default would be 0.
then the name will have to be changed to something else.
2)	Developer: I considered a tri-state option, but there didn't seem to be a good pattern for this in the Neutron code base already. Especially since BoolOpt accepts a range of equivalent values: true, True, yes, 1, etc. Implementing this logic seemed like a significant code smell.
As for the case of wanting to force disabled IPv6, having IPv6 enabled and unused is generally a no-op as opposed to not enforcing the expected IPv6 security groups. Is this worth another revision?
3)	Reviewer: a no-op? iiuc, it means we will configure ipv6 (iptables, etc.) part for nothing?
StrOpt allows None as default value (which is not a valid string), you could set default=None and let neutron chooses when the option is not explicitly set?
Source Code URL: https://review.opendev.org/#/c/196199/7..8/neutron/common/ipv6_utils.py@32
3.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I4b0d344da9ccbda79482f64535396fcc1a495d0f
Code Smell Term: code smell
Code Smell Discussions: 
1)	Reviewer: To me this case is an aberration. A mech driver test case should not inherit from a base class that pulls in the entire plugin; it's serious spillage of concerns, a massive code smell, you name it. Now that my eyes dropped here, I am increasingly thinking that this should stop.
Accidentally I saw this failure:
http://logs.openstack.org/84/150284/8/check/gate-neutron-python27/171bfb3/testr_results.html.gz
How is this related to change #150284? This is because everything is so mixed up together that a disaster is waiting to happen.
2)	Developer: this change and #150284 are independent.
3)	Reviewer: that's exactly my point! Why would we even see a failure in this test?
4)	Developer: i agree that these tests have too much code involved to be called "unit" tests.
but i'm not sure if i understand your point.  the failure log you referred looks weird but it can't be a problem of this patch.
Source Code URL: https://review.opendev.org/#/c/150239/10..11/neutron/tests/unit/ml2/drivers/test_l2population.py@101
4.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I649a62c91ce37036a16d2ea69f345581960e7079
Code Smell Term: anti-pattern
Code Smell Discussions: 
1)	Reviewer: Passing a boolean to choose between two opposite behaviors in a method is, IMO, an anti-pattern.  It makes code difficult to follow and complicates the method and its calling signature unnecessarily.
A better pattern is to abstract the common parts in to a common method and keep the specialized parts in the two methods above.  It might look something like this in pseudo-code:
  def _apply_snat_for_local_fip_traffic(self):
      rules = self._get_snat_rules_for_port()
      for rule in rules:
          self.iptables_manager...add_rule(*rule)
But, I suspect there might be even more room for improvement based on other comments.
Source Code URL: https://review.opendev.org/#/c/233334/1/neutron/agent/l3/dvr_local_router.py@307
5.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~Idf11a617369cbee0dc23c8e56ae9475bb41dcc02
Code Smell Term: anti-pattern
Code Smell Discussions: 
1)	Reviewer: Validating the number of log calls is an anti-pattern, so instead of modifying the test to pass the check, remove the check on the log calls.
2)	Developer: Done, thx for the comment.
Source Code URL: https://review.opendev.org/#/c/238136/1..2/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py@169
6.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I3cdcd0a86b77e1fd5a808b7a5f0de2057f1e90c1
Code Smell Term: anti-pattern
Code Smell Discussions: 
1)	Reviewer: This calling will change the log flag for this object and will impact following usage.
2)	Reviewer: That's a great catch Gongysh. @Brian: You should set the flag back to what it was before the call right before the return statement.
3)	Reviewer: Good to now
4)	Reviewer: amuller: since the original had this incorrect pattern, and it looks like we have the same anti-pattern at L906 of the original file and also L354 of neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py, should this patch set go forward and have a different patch set address fixing this anti-pattern?
5)	Reviewer: No, the original patch didn't have this problem because it created a temporary device, while this method acts on an existing device that might be long running in memory.
6)	Reviewer: you are correct, I missed that - n/m
7)	Developer: Done
Source Code URL: https://review.opendev.org/#/c/229010/3..4/neutron/agent/linux/ip_lib.py@233
7.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I16735195c6575454876acd0e99ef45f382963566
Code Smell Term: anti-pattern
Code Smell Discussions: 
1)	Reviewer: Having to add such a large test for a simple one-line change is an anti-pattern.  Please isolate the functionality under test so the test effort required isn't so extreme.  Feel to find me in-channel if you need help.
2)	Developer: Maru thanks for your input, please let me know what would be better to do , as I am out of ideas on this test case.
3)	Reviewer: perhaps if we split method:
remove_router_interface
at line 332 so that we have one half that does the validation and extract parameters and the other that does get on the removal, then we'll have much fewer stuff to mock up.
Bottom line is that you'd want  delete_csnat_router_interface_ports to get a non-none subnet when called by remove_router_interface.
Source Code URL: https://review.opendev.org/#/c/173001/2..3/neutron/tests/unit/db/test_l3_dvr_db.py@480
8.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I3d319e9647960d8eb8e4b85ac9e2e56e2d762ea1
Code Smell Term: anti-pattern
Code Smell Discussions: 
1)	Reviewer: there have been many discussion regarding the 'for update' lock. In general locking is an anti pattern and the 'for update' lock is proved not to work as expected in Galera for example or other HA scenario. Anyway in this case it's not really needed, a compare and swap strategy fix the issue.
Source Code URL: https://review.opendev.org/#/c/159110/3/neutron/db/db_base_plugin_v2.py@1362
9.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~Id5a8852d38543590b90e4bbed261a7a458071a9a
Code Smell Term: anti-pattern
Code Smell Discussions: 
1)	Reviewer: If abstract test cases aren't an anti-pattern, I think they should be.
2)	Developer: Done
Source Code URL: https://review.opendev.org/#/c/123000/39..41/neutron/tests/fullstack/base.py@25
10.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~Iff24fc7cd428488e918c5f06bc7f923095760b07
Code Smell Term: anti-pattern
Code Smell Discussions: 
1)	Reviewer: Why is this a class variable?  Consider providing it as an argument to setUp.
2)	Developer: If the processes fixture is an argument to setUp, then each inheriting test suite will need to override setUp just to call super with an argument. This is cleaner IMO.
3)	Reviewer: I think that having to parametize an instance via class variables is an anti-pattern and it makes for less maintainable code.  The fact that its necessary to document your intent with a 4-line comment only reinforces my argument.
4)	Developer: Will do.
Source Code URL: https://review.opendev.org/#/c/128259/25..31/neutron/tests/fullstack/base.py@43
11.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~Iee8492e9b4fcdb07eb438eb6f5958d7addeb3d8f
Code Smell Term: anti-pattern
Code Smell Discussions: 
1)	Reviewer: Duplicating the logic of the method under test is an anti-pattern.  It would be preferable for the tests to be explicit about what they were expecting, e.g.
def test_1():
 with testtools.ExpectedException(exc.PortBound):
  self._test_check...(VIF_TYPE_OVS)

def test_2():
 self._test_check...(VIF_TYPE_UNBOUND, expected=True)
2)	Developer: nice, thanks
done
Source Code URL: https://review.opendev.org/#/c/162500/10..11/neutron/tests/unit/ml2/test_ml2_plugin.py@466
12.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I280948300810f199988a943aeb6577243423a4e7
Code Smell Term: anti-pattern
Code Smell Discussions: 
1)	Reviewer: Can you explain under what flow would this code be invoked? If appropriate I think a functional test would be a thousand times more effective.
This test (And similar tests in this file) all assert that certain methods were called. I don't know why we'd want to do that when we can just test state instead via a functional test. It just seems weird to mock out all of this stuff when there's no need. Why do I care which methods were called? What if I want to move those function calls around? Why should I change a million unit tests in order to do that? If a function was called, does that mean it was called with the right parameters? Does it mean it actually did what I expected it to do? If I'm trying to test the removal of floating IPs from DVR routers, why do I care what methods it called internally in order to do that? The more code you mock out, the less you run in the actual test.
Or as Terry said last week, these tests remind me of clippy from Word in the 90's: Uh oh! It seems like you changed the internal implementation of some method! Are you sure? (N/y). Sadly these tests detect change but they don't validate anything.
2)	Developer: One way this code would be invoked would be through an agent restart.  The rtr_fip_subnet would not have been initialized yet.  A functional test handling agent restart in general would be good, I agree.  But one of the reasons for UTs is to catch harmful code changes isn't it?  For this simple one line fix, a simple UT seemed appropriate.  But thats just my vote.
3)	Reviewer: I think that a functional test is simpler. You don't have to tinker with all of this mocking. We already have a test that simulates restarting an agent (test_ha_router_conf_on_restarted_agent). Can I ask you to consider a functional test? Think if one would be possible and elegant, and if not, please yell at me with specifics of why not.
4)	Reviewer: Where mocking is required, I don't think decorators are suggested.  I'd much rather see inline-mocking for consistency with existing code.
I'd also agree with Assaf's assertion that functional testing is where this check should be performed.  Having to add so many lines of unit test code to validate a small code change is an anti-pattern and I'd rather see coverage of actual system interaction.
Source Code URL: https://review.opendev.org/#/c/157621/4..5/neutron/tests/unit/agent/l3/test_dvr_router.py@116
13.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I6db1e7ffef5e1243478e15a994787396b0908656
Code Smell Term: anti-pattern
Code Smell Discussions: 
1)	Reviewer: Is there a reason to spawn at init rather than lazily when test_connectivity is called?  I'm pretty sure doing potentially costly operations on object creation is an anti-pattern.
2)	Developer: Done
Source Code URL: https://review.opendev.org/#/c/120349/17..18/neutron/tests/functional/agent/linux/helpers.py@140
14.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~Ia5071552239c9444c5105a150b268fb0437e4b85
Code Smell Term: anti-pattern
Code Smell Discussions: 
1)	Reviewer: IE: -> i.e.
Also, the sentence starting with 'Thus' is duplicating what appears in code and tests - please remove.  If you think this explanation is necessary, I would encourage you to refactor lines 232-237 until that is no longer the case, because comments duplicating code is an anti-pattern.
2)	Developer: Done
Source Code URL: https://review.opendev.org/#/c/142843/6..8/neutron/agent/l3/ha.py@222
15.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I56045ede3a3dc1a7044a22913ee38ed382a81052
Code Smell Term: anti-pattern
Code Smell Discussions: 
1)	Reviewer: The need to validate behavior in code that is under ones control is an anti-pattern that usually indicates poorly structured code.  And yes, lots of Neutron tests exhibit this flaw, but that's not a reason to perpetuate it.
Reference: http://googletesting.blogspot.com/2013/03/testing-on-toilet-testing-state-vs.html
Source Code URL: https://review.opendev.org/#/c/139932/8..10/neutron/tests/unit/test_l3_agent.py@1027
16.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I94aaa0bef68e407649b421db53bb59d007335f5c
Code Smell Term: anti-pattern
Code Smell Discussions: 
1)	Reviewer: (No action required) Consider reversing this change.  Inline methods (except in extremely specialized circumstances) are an anti-pattern for me because they are effectively untestable.  The only reason I'm not going to block on this is that this is a non-functional refactor and we will hopefully be deprecating the cli interface to ovsdb before this code would be the subject of any modification.
Source Code URL: https://review.opendev.org/#/c/141134/8..13/neutron/agent/linux/ovs_lib.py@130
17.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~Ie653757370d9f76b70f747a799a71c1af2406cf5
Code Smell Term: anti-pattern
Code Smell Discussions: 
1)	Reviewer: Behavior verification is an anti-pattern - isn't there another way you can test this?
See: http://googletesting.blogspot.com/2013/03/testing-on-toilet-testing-state-vs.html
2)	Developer: This case is somewhat similar to what is presented in the latest point of your linked article (the MVC case). The final state of the tested method is actually determined by the "ensure_path_created_for_port" call, which is a method defined in a vendor (apicapi) library which ultimately calls the backed APIs. So its behavior is abstracted from the logic of the tested method.
3)	Reviewer: I agree with Ivar here. It seems like ensure_path_created is pushing the end state into what is essentially a black box.
Source Code URL: https://review.opendev.org/#/c/144796/1/neutron/tests/unit/ml2/drivers/cisco/apic/test_cisco_apic_mechanism_driver.py@101
18.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~Ie6eae4cecb64120c41de9823d9e72066094ad2ce
Code Smell Term: anti-pattern
Code Smell Discussions: 
1)	Reviewer: Whenever a test addition is significantly more complex than the code it targets, I smell anti-pattern.  As per my comment on the previous patch, here is what I would suggest:
# Extracted out of delete_network
def _delete_ports(self, context, ports)
 for port in ports:
   self.delete_port(context, port.id)
 ...

def test_delete_ports_ignores_missing_port(self):
 plugin = manager.NeutronManager.get_plugin()
 with mock.patch.object(plugin, 'delete_port', side_effect=exc.PortNotFound()):
plugin._delete_ports(None, [mock.MagicMock()])
2)	Developer: oh, you're suggesting refactoring delete_network method itself.
Yep, that looks better.
Source Code URL: https://review.opendev.org/#/c/143698/2..3/neutron/tests/unit/ml2/test_ml2_plugin.py@122
19.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~Ie6eae4cecb64120c41de9823d9e72066094ad2ce
Code Smell Term: anti-pattern
Code Smell Discussions: 
1)	Reviewer: I really would like to stop this habit of asserting that logs are being called... It's an anti pattern if I've ever seen one.
2)	Developer: Agree, here it is not necessary for verification.
3)	Reviewer: +1 - PortNotFound being raised but not re-raised should be enough of an indication that the intended codepath has been followed.
I'm more upset at the continued use of the wsgi layer for testing.  Consider extracting out port and subnet deletion so they can be tested directly.
4)	Developer: Done
Source Code URL: https://review.opendev.org/#/c/143698/1..2/neutron/tests/unit/ml2/test_ml2_plugin.py@128
20.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I6253f2e3dc3c723eadf17a9ed40c7ddbdb4627b1
Code Smell Term: anti-pattern
Code Smell Discussions: 
1)	Reviewer: I'd add: don't simply mimic the pattern of other tests assuming they represent the 'right' way to test. We are working hard to move away from some of them. If you identify some of the anti-patterns outlined above, then be critical and question whether what you read is good code and strive for a better approach if you can. Reach out to the testing LT's and the team on IRC for feedback
2)	Reviewer: Done
Source Code URL: https://review.opendev.org/#/c/230215/1..2/doc/source/devref/effective_neutron.rst@95
21.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~Ibd2dac02c5a93bb391b620cc27bc46a6cdf6d817
Code Smell Term: bad pattern
Code Smell Discussions: 
1)	Reviewer: As per https://review.openstack.org/#/c/147312/ , it's a bad pattern and may not work with oslo.log. For more details, see "No more implicit conversion to unicode/str" section in the patch I referred to.
Source Code URL: https://review.opendev.org/#/c/166294/1..2/neutron/cmd/sanity/checks.py@168
22.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~Id0822f1c3d586c813a09e4b442a21d3cb52943d0
Code Smell Term: bad pattern
Code Smell Discussions: 
1)	Reviewer: this could've been done once...I am starting to see a bad pattern here..
Source Code URL: https://review.opendev.org/#/c/142863/47..48/neutron/tests/unit/test_legacy_router.py@40
23.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I514cae0fa43033433ec2982bcf3726e02e6692bf
Code Smell Term: bad pattern
Code Smell Discussions: 
1)	Reviewer: This solution doesn't seem compliant  with galera multi-writers. Thoughts?
2)	Developer: Can you shed some light on issues with galera multi-writers?
As I got from brief search:
The SQL construct "select ... for update" can cause deadlocks with galera multi-writers. Would it be enough to add retry_on_deadlock wrapper? Or some better way exists for dealing with galera?
3)	Reviewer: I'd really prefer something *other* than retry_on_deadlock - that's already a big piece of technical debt and adding to it doesn't excite me
4)	Reviewer: It depends :)
Typically if you select for update on a table in order to update an other table then no deadlock is raised as no update is done on "selected for update" rows ... an example[1] and how to handle such case[2].
In this case, i am not sure on what the select for update is applied because of the join: IpamAvailabilityRange or/and IpamAllocationPool:
If it's on IpamAllocationPool then no update is done on selected rows => not working
Otherwise an update is done on selected rows so if 2 concurrent calls try to update the same range then the last one to call session commit will raise a deadlock
[1] https://review.openstack.org/179955
[2] https://review.openstack.org/187985
5)	Reviewer: IMO, the right approach is to split the fix in 2 changes: one handling the non-galera case (simple and quick to merge), one handling the galera case (often, imply many iterations)
6)	Reviewer: Technical debt? 
A "normal" backend locks the rows when the select for update is done and disallows "overlapping" concurrent select for update. A multi-writer galera backend is not able to ensure such lock and instead raises a deadlock for every session in conflicting ("overlapping") with a committed session ... so we have to handle such deadlock or replace range by a table with every allocatable ip (outch)
7)	Developer: IpamAllocationPool is going to be changed in this "select for update".
Agree about splitting fix into 2 stages. Using 'with_lockmode('update')' is quite popular way of locking in this file, so it seems to be a long way to make it compliant with galera. 
Investigating with examples.
Source Code URL: https://review.opendev.org/#/c/223123/1..2/neutron/ipam/drivers/neutrondb_ipam/db_api.py@153
24.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~Ieb2796381579ad295abf361ce483d979a53d2bd6
Code Smell Term: technical debt
Code Smell Discussions: 
1)	Reviewer: as suggested in earlier PS, a separate bug is better for tracking purpose.
2)	Reviewer: This script is a direct relation to the bug above, what is the point in opening a new bug for this?  In that bug, the automatic empty bridge cleanup was removed, which means you have to manually remove those.  This doesn't close bug 1328546 but it's obviously related.  Any new bug would just say bug 1328546 leaves old empty bridges around and we should provide a tool to remove them, so it seems like busy work.
3)	Reviewer: Ideally such bug should have been created by the one fixing related bug as a tech debt bug.
4)	Developer: Done. I hope people will open technical debt bugs in the future so we can better track them.
Source Code URL: https://review.opendev.org/#/c/221508/7..8//COMMIT_MSG@26
25.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I83ded140b37a4d276ab64911d73d4224b598c33b
Code Smell Term: technical debt
Code Smell Discussions: 
1)	Reviewer: Netaddr has an IPRange object that should be used.
2)	Reviewer: Either that or an IPSet object. Either way I'd suggest using the Netaddr library rather than writing your own
3)	Developer: Done
4)	Developer: agree, thanks for your suggestion and review!
5)	Reviewer: +1 to Sean's suggestion. Even if you can't find a generator in the netaddr lib, it's not worth the technical debt of the extra code here since the result of this function is just iterated over immediately with the ips collected into a commands list anyway.
Source Code URL: https://review.opendev.org/#/c/146778/1/neutron/agent/linux/iptables_firewall.py@566
26.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I8c485e74ae06b10fd284797359bc6d1115361713
Code Smell Term: technical debt
Code Smell Discussions: 
1)	Reviewer: I'll be honest it's *very* hard to know if this design fullfils the requirements on HEAD, and in Michael's patch. Will it be possible to start moving stuff into the Router classes and have everything still nice and clean? I don't know. I'd like to see a dependent patch that moves 3 pieces of code, as examples, to prove that we're on the right path, *before* this patch is merged:
  1) Something that happens if the router is not distributed nor HA.
  2) Something that happens if the router is only HA.
  3) Something that happens if the router is only distributed.
  4) Something that happens if the router is both distributed and HA (This will happen in Michael's patch, separate from the above mentioned follow up).
My second point, which I wrote on Michael's patch as well here: https://review.openstack.org/#/c/139686/5//COMMIT_MSG (Second paragraph)
First get Micahel's patch working functionallity, so we understand what we *need* (Basically additional requirements), then we make the necessary changes (Hopefully none) to this patch, then depend on it.
2)	Developer: If we insist on getting Michael's patch working before this can merge then we must put refactoring on hold for weeks or months (please, let's not be overly optimistic here).  Making it a dependency of this refactoring effort will stall this effort to beyond what we can accomplish in the Kilo timeframe.
Michael's patch is *new* functionality.  While I understand the desire to be sure that this will support the new functionality, I'm not concerned enough to change direction like this.  The priority for this cycle is paying down technical debt over adding new functionality.
3)	Developer: I might have gone a little overboard with the weeks or months thing.  However, I still say it doesn't make sense to hold off the refactoring until we've added the functionality.  Debt payment is a priority.
If you are concerned with how something will work specifically then let's discuss it.  However, I'm not concerned that we're going to paint ourselves in to a corner here.  We'll figure out how to make it work.
4)	Reviewer: I did not mean for this patch to depend on Michael's patch. I meant that, if an additional 1 or 2 weeks will make a different, we should wait until Michael has something that's working (Not perfect, not mergeable, without tests, but working). The alternative is for Michael to depend on this patch and just see if it's workable.
Source Code URL: https://review.opendev.org/#/c/143733/8..9//COMMIT_MSG@6

1.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~Ia7e67ffd23a114dc380edde1c70af0974b1e85cd
Code Smell Term: code smell
Code Smell Discussions:
1)	Reviewer: This is pollution.
It's bad already that other stuff that doesn't belong here ended up being here. I have clearly stated already that in lieu of a mechanism for cleanly passing this info to the agent from the core plugin, we should accept the cost of an extra call for fetching trunk information. To avoid the cost, let's figure out how to avoid this because it's clearly a code smell.
Source Code URL: https://review.opendev.org/#/c/337896/2/neutron/plugins/ml2/rpc.py@128
2.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I6740e50e8594623f627ae5355a378e1ad8bdb03c
Code Smell Term: anti-pattern
Code Smell Discussions:
1)	Reviewer: I don't like that we set 3 knobs while one is configurable. I think it's better how it was before where you have a dedicated namespace and kernel option per namespace is configured based on which namespace class you use.
2)	Developer: The kernel option is still configured based on which namespace class you use, it just defaults to 0.
The issue with before is that it messes with initialisation of objects, creating 2 Namespace objects at times and discarding the first since it's "not the HA namespace we need".
3)	Reviewer: So what is the issue? You have a subclass that replaces an attribute of superclass with more specific object and throws the original attribute away. I don't see anything wrong with that.
4)	Developer: The issue is that if we get to the point where 2 different classes (in the same hierarchy) create a Namespace object, something is wrong. Imagine someone in the future changes the Namespace.__init__ to actually interact with the operating system somehow - this specific case will cause 2 different interactions where only one is intended.
5)	Reviewer: Having system interaction in __init__ is an anti-pattern exactly for this reason. That's why we have create() methods to avoid such side-effects.
6)	Reviewer: I don't think replacing an attribute is the best we can do. Ideally, we would not even need create()s in subclasses, leaving the parent to behave depending on arguments we pass to __init__ (like we do for use_ipv6).
Source Code URL: https://review.opendev.org/#/c/396002/1/neutron/agent/l3/namespaces.py@92
3.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I26453eb9a1b25e116193417271400994ac57e4c1
Code Smell Term: bad pattern
Code Smell Discussions:
1)	Reviewer: Thinking about this I am starting to believe we're overloading a utility function that's not quite meant to be used for controlling features like trunking but I can see that we've been copying a bad pattern. I would have personally added a [1] to devstack/settings and did [2]. This way it's clear that trunk is not yet another service that runs alongside neutron's backbone.
 [1] TRUNK_ENABLED=$(trueorfalse True TRUNK_ENABLED)
 [2] if [[ $TRUNK_ENABLED == True ]] ; then # do what you have to do.
We may want to clean the rest up, but that's another story.
As for the gate configuration, the hook should simply set TRUNK_ENABLED=True. 
The outcome is the same and I am questioning the style here. I am not strongly opinionated at this point.
Source Code URL: https://review.opendev.org/#/c/320092/23..24/devstack/plugin.sh@26
4.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I0d7e1cd9fc075902449c5eb5ef27069083ab95d4
Code Smell Term: bad pattern
Code Smell Discussions:
1)	Reviewer: It's better to use the pattern "self.assertEqual(expected, observed)" so that's self.assertEqual(v, rule['security_group_rule'][k]).
2)	Developer: true, but this is consistent throughout the file, which means we copied a bad pattern all over the place ;)
Source Code URL: https://review.opendev.org/#/c/294460/4/neutron/tests/unit/extensions/test_securitygroup.py@857
5.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~Ia35e8a946bf0ac0bb085cde46b675d17b0bb2f51
Code Smell Term: bad pattern
Code Smell Discussions:
1)	Reviewer: Please! you don't need to covert keys in list to check if it contains that is a O(N) peration. dict implements __contains__ method that should be O(1).
    self.assertIn(plugin, svc_plugins)
This could not work on python3 because keys() probably returns an iterators instead of a list.
2)	Developer: sorry, I copied a bad pattern without my brain switched on.
Source Code URL: https://review.opendev.org/#/c/273439/5..6/neutron/tests/unit/test_manager.py@118
6.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I498560798983177ce7b64e1a8f32f1a157558897
Code Smell Term: technical debt
Code Smell Discussions: 
1)	Reviewer: aloha! This shouldn't be necessary. It's just like when a VM port is plugged in and it doesn't have a vlan tag set. The agent loop will find it an treat it as a new port, at which point it will do it's standard get_device_details, get the network, allocate a local vlan tag, and set the local vlan itself. So the trunk manager doesn't have to concern itself with those details. It's a manager, it delegates. :)
2)	Developer: hey Kevin, the agent loop ignores these interfaces, see lines 41. That was done on purpose, to keep the trunk processing separated and avoid regressions.
3)	Reviewer: So, I think here we have two strategies laid out ahead of us based on two premises:
A) A subport is just like a regular port, and as such it will never need to be treated differently, EVER. Under this premise, delegation would be justified. With this strategy the trunk component could create the patch port into br-int with external-ids(iface-id, iface-status, and attached-macs), and have the OVS agent pick it up and do its thing. If in the future, we end up finding ourselves in a position where subports do need to be treated specially, then we would be in a bit of a jam because there's no strong contractual agreement between trunk manager and the ovs agent and we might potentially add more technical debt to the mix, unless we step back and redesign. That's a position I'd rather not be in, but Kevin seems adamant that this is never going to happen.
B) A subport might or might not be a regular port, and we'd want to give ourselves the opportunity to put the trunk manager in the position of dictating how the subport wiring is done. We currently don't have a strong API in the OVS agent to do that, and in order to achieve this goal we would need to pay some technical debt now, in the hope that it can be beneficial in the future, should the use case arise. Paying technical debt now can slow us down because we effectively need to think harder about the interfaces required between the trunk component and the other OVS components and how they need to collaborate in order to provide a complete L2 wiring setup, including DVR flows, security groups, spoofing rules, etc. If the use case does not arise (premise A), we may have made the trunk component overblown for nothing. This is the position I have been leaning towards and the direction Rossella's patch is going. It assumes that by means of a stronger contractual agreement across the various components, there's a smaller change of unexpected regressions should one logic or the other require to evolve.
Both A and B are sensible approaches in my view, and both have pros and cons. The choice of one over the other is made on how risk averse one wants to be. Option A seems easier to attain in the short term, while Option B requires more work in the long run, and it forces us to explicitly document the things we know won't work with trunks and OVS right out of the box (e.g. security groups?)
Perhaps we can go down path B for a tad farther just to see how deep the hole is and decide to fall back on path A, if we do end up meeting the white rabbit.
Thoughts?
Source Code URL: https://review.opendev.org/#/c/335536/5..6/neutron/services/trunk/drivers/openvswitch/agent/trunk_manager.py@49
7.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I54fe3b4a291879e5062feb0c65c843591100e716
Code Smell Term: technical debt
Code Smell Discussions: 
1)	Reviewer: Will there be any advantage of using the 'vif_type' rather then the device_owner.
I would recommend that we do not deviate too much and make any changes unportable to older versions by fixing this.
2)	Developer: using the 'vif_type' rather then the device_owner is needed for HA portbinding. With this HA will also set vif_type to distributed and use existing DVR portbinding i.e existing portbinding will be generic and will be used for all DVR, HA and HA+DVR cases.
Patch for HA to use DVR portbinding
https://review.openstack.org/#/c/323314/
I can't use device_owner as I explained in my previous comment(main reason is - port with DEVICE_OWNER_ROUTER_SNAT is a distributed port when router is HA + DVR and not a distributed port when router is only DVR. SO device_owner is not useful for this port's binding)
Copying my previous comments here - 
Why I am using vif_type instead of device_owner?
These are the distributed router ports
1) port with device owner DEVICE_OWNER_DVR_INTERFACE - when a router is only DVR
2) port with device owner DEVICE_OWNER_HA_REPLICATED_INT - when a router is only HA
3) ports with device owner DEVICE_OWNER_DVR_INTERFACE,  DEVICE_OWNER_ROUTER_SNAT - when a router is HA + DVR
So port with DEVICE_OWNER_ROUTER_SNAT is a distributed port when router is HA + DVR and not a distributed port when router is only DVR.
So I can't rely only device_owner and instead set vif_type based on device_owner and some other parameters i.e https://review.openstack.org/#/c/323314/12/neutron/plugins/ml2/plugin.py L333
There are two reasons for using vif_type
1) As you see we check for device_owner at many places in the code. If we are replacing that with the method[1], unnecessarily we are introducing a method call at all these places, which can be avoided with simple vif_type check. Making this function generic and accessible from all the places(l2 plugin, l3 plugin, l2pop driver, l3 rpc api and rpc handlers, etc) in the code is also not easy. One more thing is, every time this method is called, there is a DB access. So I feel checking for vif_type is simple.
2) If you see, dvr ports are already setting vif_type to distributed as part of port_dict. That means wherever port_dict is used in the code(and also user api requests) they are using vif_type as distributed.  I don't know why they are not doing that in ml2_port_bindings table. It might be a bug. If this change is not done then
1) vif_type shown to users through api will be distributed
2) vif_type in ml2_port_bindings will be unbound(and never used anywhere in the code)
3) vif_type in ml2_distributed_port_bindings can be unbound, failed or mechanism specific vif_type(like ovs).
At least I am avoiding inconsistency between what user sees as part of API and what is in ml2_port_bindings. With vif_type=distributed in ml2_port_bindings, this table shows that it is a distributed port and actual binding of the table will be available in ml2_distributed_port_bindings table.
[1] https://review.openstack.org/#/c/323314/12/neutron/plugins/ml2/plugin.py L333
3)	Reviewer: My only concern with these changes are the issues with backporting.
4)	Reviewer: It's the same discussion we always have around refactors, shall we break backports (make them more difficult) or shall we live with technical debt forever?.
IMHO fixing technical debt specially when it's related to make features more interoperable it's really worth, and while it adds some extra pain in the face of backporting patches, we should do it.
Source Code URL: https://review.opendev.org/#/c/340031/3..4//COMMIT_MSG@21
8.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~Ie96b52dbe3a1f32cd4c11de8d8a5eff663fbf7f6
Code Smell Term: technical debt
Code Smell Discussions: 
1)	Reviewer: and this one?
2)	Developer: I will start another patch to see improve this. It feels good to kill technical debt of any kind, specially when its yours.
@Ihar, as what do you think of a decorator to expose objects and kill this dependency chain...
something like....
from neutron.api.rpc.callbacks import resources
@resources.expose_via_rpc_callbacks
class MyNeutronObject(......):
3)	Reviewer: I think it's fair to introduce a decorator for that. Actually, we may even have it replacing existing registration decorator from oslo.versionedobjects, so that it handles ovo registration as well as rpc callbacks one (probably based on a class attribute defined on the class).
4)	Developer: Sounds reasonable:
have a general ovo registration decorator, and a flag for RPC-callback enablement.
I'll do that.
Source Code URL: https://review.opendev.org/#/c/322857/2..3/neutron/api/rpc/callbacks/version_manager.py@41
9.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~Icec56676d83b604c3db3377838076d6429d61e48
Code Smell Term: technical debt
Code Smell Discussions: 
1)	Reviewer: I still am not really excited by this, but on the other hand I am guilty of not having worked to a better solution.
The reason that it does not excite me is that there's information redundancy with resource_id.
But don't worry - it's a rather small bit of technical debt to repay.
Put it on my tab, please.
2)	Reviewer: agreed, its definitely hacky.  However, its not that the resource_id isn't available, its that those particular controllers expect kwargs by specific names, so it's hard to genericize it.  I think you get that, though.
Source Code URL: https://review.opendev.org/#/c/267985/9..10/neutron/pecan_wsgi/controllers/resource.py@89

1.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I5721a9ee35235c15bef1a9e8d62568abbfb5dc45
Code Smell Term: anti-pattern
Code Smell Discussions:
1)	Reviewer: I think existing options may be enough to achieve what you need. Specifically, just inspect that available_type_drivers have two distinct types known to have different overhead requirements (e.g. vxlan and gre; or vlan and gre; or vlan and vxlan; ...) and use the identified pair to achieve different MTUs. With the ongoing work in Pike to allow to specify mtu on network creation and update [1], it will be even simpler, but for now, I think inspection of the list of available drivers is the way to go.
The benefit of reusing the existing option and not adding a new one is that existing tempest deployments that already rely on the existing option may get the test triggered for them with no change in deployment tooling that would otherwise need to set the new mtu_test_support option.
Also, a test-specific option is an anti-pattern in tempest. Instead of a knob per test class/case, it's better to describe the cloud setup in general terms (whether it supports this or that feature; whether its api allows for X or Y; ...) and then rely on this information to determine which test cases can be executed.
Tell me what you think about relying on available_type_drivers.
2)	Developer: Uploading the patch that doesn't rely on it with GRE and VXLAN, let's check that this theory is working
Source Code URL: https://review.opendev.org/#/c/481928/3..4/neutron/tests/tempest/config.py@33
2.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I2ecac93db0ab867860e796248b5655f4cece6da5
Code Smell Term: technical debt
Code Smell Discussions: 
1)	Reviewer: Responding to your reply on patch 13: In that case, we should hold off on these changes entirely until the common framework has merged. Otherwise we're just introducing technical debt.
Source Code URL: https://review.opendev.org/#/c/371000/14/doc/source/devref/testing_coverage.ini@11
3.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I690d8e69a34294a99997abbc4e1b749515c2700b
Code Smell Term: technical debt
Code Smell Discussions: 
1)	Reviewer: it's probably just me, and I fully admit iptables manager was never my expertise, but I miss the logic behind those wrap= arguments included in the patch. Could you please clarify that in the commit message what's the goal here?
(Looking at all the places where you pass wrap=False) Is there any place in the code where we actually want to wrap now? Why?
I understand a log of ugliness here probably comes from the technical debt we already have in the code...
2)	Developer: The wrap mechanism was introduced with the idea of allowing different processes to create their own chains without stepping on each others rules. Wrapping (by default) prefixes the chain name with the binary name of the process  
But there are 2 problems:
1. Today we don't have separate processes but different components of the same process creating the rules(FWaaS v2 and SG are both applied by l2 agent process). In this patch I am overriding the default wrap name with component prefixes.
2. There was no common jump point to branch out to different tables for rule processing. e.g. the INPUT chain should jump to common chain, from common the control can jump to FWaaS and SG specific tables for processing and finally converge back to a common ACCEPT table that should allow traffic. The common chains cannot be wrapped as they are common to both components hence a lot of calls have the wrap=False.
We started with the simplest implementation, but are open to ideas and collaboration, and all feedback (as you already pointed out about flexibility) are very welcome.
Source Code URL: https://review.opendev.org/#/c/348177/30..31/neutron/agent/linux/iptables_firewall.py@226

1.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I4944041df81e24683bc612560808bcdcc2db6bf2
Code Smell Term: technical debt
Code Smell Discussions: 
1)	Reviewer: since notify is marked deprecated [1]. already, I think this will increase technical debt for work already in progress to use publish and payload across all networking projects. I'd suggest using publish and payload here.
[1]. https://github.com/openstack/neutron-lib/blob/master/neutron_lib/callbacks/registry.py#L53
2)	Developer: You are right, I'll change it.
Source Code URL: https://review.opendev.org/#/c/611059/2..3/neutron/db/l3_db.py@1310
2.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I26e22bce7edd1f93b2ac0048b61b14f858938537
Code Smell Term: technical debt
Code Smell Discussions: 
1)	Reviewer: I recall, this notification_driver option was deprecated, I doubt this will add interest to existing technical debt, but need to verify
2)	Developer: There is no deprecated mark:
https://github.com/openstack/neutron/blob/master/doc/source/admin/archives/adv-operational-features.rst
3)	Reviewer: my bad i think it was just for qos, the use of notification driver was deprecated
Source Code URL: https://review.opendev.org/#/c/424468/13..14/neutron/tests/unit/extensions/test_qos_gateway_ip.py@133
3.	Code Change URL: https://review.opendev.org/#/q/openstack/neutron~master~I4e20146290686b38c4f8b8736f1d9ee0525c1034
Code Smell Term: technical debt
Code Smell Discussions: 
1)	Reviewer: This is doing more than removing the py27 neutron-fullstack job. It also changes the parent of neutron-fullstack-python36 job from neutron-fullstack to legacy-dsvm-base. Is it what you intend to do?
2)	Developer: It is what I intend to do.  You correctly identify the job being removed (neutron-fullstack) as the parent of the neutron-fullstack-python36 job which will remain.  To leave neutron-fullstack-python36 unchanged would have required me to leave the definition of the now-defunct job in place, which seemed to be incurring technical debt/cruft.  Instead I remove the defintion of the job being removed, which means adding to neutron-fullstack-python36 the elements it was inheriting from neutron-fullstack so that neutron-fullstack-python36 can have legacy-dsvm-base as a direct ancestor.  
This seemed like the cleanest solution.
3)	Reviewer: Yes, this is fine.
Source Code URL: https://review.opendev.org/#/c/605126/1..2/.zuul.yaml@121
